home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Linux Cubed Series 7: Sunsite
/
Linux Cubed Series 7 - Sunsite Vol 1.iso
/
system
/
admin
/
linuxcon.000
/
linuxcon
/
linuxconf-1.6
/
netconf
/
exports.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-07-19
|
10KB
|
444 lines
#include <string.h>
#include <stdlib.h>
#include "../misc/misc.h"
#include "../xconf/xconf.h"
#include "netconf.h"
#include "netconf.m"
static NETCONF_HELP_FILE helpf ("exports");
CONFIG_FILE f_exports (ETC_EXPORTS,helpf
,CONFIGF_MANAGED|CONFIGF_OPTIONNAL|CONFIGF_PROBED);
/* #Specification: /etc/exports / format
The /etc/exports file separate lines for each portion of the
file system exported. The format of each line is
#
mount_point client_host[(option)] [ client_host[(options) ...]
#
*/
class HOST_OPT: public ARRAY_OBJ{
public:
char *host;
char root; // Root access allowed
char link_relative;
char rw;
/*~PROTOBEG~ HOST_OPT */
public:
HOST_OPT (const char *_host, const char *options);
void write (char *buf);
~HOST_OPT (void);
/*~PROTOEND~ HOST_OPT */
};
PUBLIC HOST_OPT::HOST_OPT (const char *_host, const char *options)
{
host = NULL;
root = 0;
link_relative = 0;
rw = 0;
host = strdup(_host);
if (options != NULL){
while (1){
const char *start = options = str_skip (options);
if (*options == '\0'){
break;
}else{
while (*options > ' ' && *options != ',') options++;
int len = (int)(options - start);
if (strncmp(start,"rw",len)==0){
rw = 1;
}else if (strncmp(start,"ro",len)==0){
rw = 0;
}else if (strncmp(start,"no_root_squash",len)==0){
root = 1;
}else if (strncmp(start,"root_squash",len)==0){
root = 0;
}else if (strncmp(start,"link_relative",len)==0){
link_relative = 1;
}else if (strncmp(start,"link_absolute",len)==0){
link_relative = 0;
}else{
fprintf (stderr,"Unknown option %s\n",start);
}
options = str_skip(options);
if (*options == ',') options++;
}
}
}
}
PUBLIC HOST_OPT::~HOST_OPT()
{
free (host);
}
static char exports_wopt (const char *str, char *buf, char sep)
{
if (str[0] != '\0'){
int len = strlen(buf);
buf[len++] = sep;
sep = ',';
strcpy (buf+len,str);
}
return sep;
}
/*
Format a host export specification like in /etc/exports
*/
PUBLIC void HOST_OPT::write (char *buf)
{
buf = strcpy (buf,host);
/* #Specification: /etc/exports / default values
When writing back the /etc/exports file, we try to avoid
the generation of default value for option. The idea is
to keep the file readable by avoiding long option name.
We are making an exception for option ro/rw, because it
is terribly important and because it is a short one.
*/
// The keyword associated with the default behavior is set to ""
// so we know we don't have to generate it.
char sep = '(';
static char *tb_rw[]={"ro","rw"};
sep = exports_wopt (tb_rw[rw],buf,sep);
static char *tb_root[]={"","no_root_squash"};
sep = exports_wopt (tb_root[root],buf,sep);
static char *tb_link[]={"","link_relative"};
sep = exports_wopt (tb_link[link_relative],buf,sep);
if (sep == ',') strcat (buf,")");
strcat (buf," ");
}
class HOSTS_OPT: public ARRAY{
/*~PROTOBEG~ HOSTS_OPT */
public:
void add (const char *_host, const char *_option);
HOST_OPT *getitem (int no);
char *parse_add (const char *pt, int &err);
void write (FILE *fout);
/*~PROTOEND~ HOSTS_OPT */
};
PUBLIC void HOSTS_OPT::add (const char *_host, const char *_option)
{
HOST_OPT *opt = new HOST_OPT(_host,_option);
if (opt != NULL) ARRAY::add (opt);
}
/*
Parse a string to extract a host(option) spec.
Return a pointer after the spec. If the spec is valid, it is
add to the array.
*/
PUBLIC char *HOSTS_OPT::parse_add (const char *pt, int &err)
{
char tmp[200];
char *dst = tmp;
while (*pt > ' ' && *pt != '(') *dst++ = *pt++;
*dst = '\0';
pt = str_skip(pt);
if (*pt == '('){
pt++;
if (dst == tmp){
// Host name empty ???
err = 1;
}else{
char opt[1000];
dst = opt;
while (*pt != '\0' && *pt != ')') *dst++ = *pt++;
*dst = '\0';
if (*pt == ')') pt++;
add (tmp,opt);
}
}else if (dst != tmp){
add (tmp,NULL);
}
return (char*)pt;
}
PUBLIC HOST_OPT *HOSTS_OPT::getitem(int no)
{
return (HOST_OPT *)ARRAY::getitem(no);
}
PUBLIC void HOSTS_OPT::write (FILE *fout)
{
for (int i=0; i<getnb(); i++){
char buf[200];
getitem(i)->write(buf);
fputs (buf,fout);
}
}
class EXPORT_FS: public ARRAY_OBJ{
char *comment;
char *path;
HOSTS_OPT tbhost;
/*~PROTOBEG~ EXPORT_FS */
public:
EXPORT_FS (const char *buf, int noline);
int edit (void);
const char *getpath (void);
void write (FILE *fout);
~EXPORT_FS (void);
/*~PROTOEND~ EXPORT_FS */
};
/*
Parse one line of /etc/exports
*/
PUBLIC EXPORT_FS::EXPORT_FS (const char *buf, int noline)
{
/* #Specification: /etc/exports / configurator
The configurator remember comments, either full line or
at the end of a line.
It read it and write it back. This means that /etc/exports
may still be edited by hand or by netconf.
*/
char *pt = str_skip(buf);
comment = NULL;
path = NULL;
if (*pt == '#'){
comment = strdup(str_skip(pt+1));
}else if (*pt != '\0'){
char tmp[1000];
pt = str_copyword (tmp,pt);
if (tmp[0] != '\0'){
path = strdup(tmp);
int err = 0;
while (1){
pt = str_skip (pt);
if (*pt == '#'){
comment = strdup(str_skip(pt+1));
break;
}else if (*pt == '\0'){
break;
}else{
pt = tbhost.parse_add (pt,err);
}
}
if (err){
xconf_error (MSG_U(E_IVLEXPORTS
,"Invalid line %d in file %s\n%s\n")
,noline,ETC_EXPORTS,buf);
free (path);
path = NULL;
free (comment);
comment = strdup(buf);
}
}
}
}
PUBLIC EXPORT_FS::~EXPORT_FS ()
{
free (comment);
free (path);
}
PUBLIC const char *EXPORT_FS::getpath()
{
return path == NULL ? "" : path;
}
PUBLIC void EXPORT_FS::write (FILE *fout)
{
if (path != NULL){
fprintf (fout,"%s ",path);
tbhost.write (fout);
fputc (' ',fout); // Put a space for the comment in case.
// Look nicer
}
if (comment != NULL){
fprintf (fout,"# %s",comment);
}
fputc ('\n',fout);
}
/*
Edit the spec of one export directory
*/
PUBLIC int EXPORT_FS::edit()
{
const int NBFIELD = 7;
const int F_comment = NBFIELD-1;
const int F_path = 0;
char tb[NBFIELD][MAX_LEN+1];
memset (tb,'\0',sizeof(tb));
if (path != NULL) strcpy (tb[F_path],path);
if (comment != NULL) strcpy (tb[F_comment],comment);
/* Not very good because it assume a fixe number of host */
/* this part is only a prototype as the user interface part is */
/* not flexible enough */
for (int i=0; i<tbhost.getnb() && i < F_comment -1; i++){
tbhost.getitem(i)->write(tb[i+1]);
}
int nofield = 0;
int ret = -1;
while (1){
static const char *prompts[NBFIELD]={
MSG_U(F_PATHEXP,"Path to export"),
MSG_U(F_CLIHOSTS,"Client hosts + option"),
" ",
" ",
" ",
" ",
MSG_U(F_COMMENT,"Comment (opt)")
};
if (xconf_multiinp(MSG_U(T_ONEEXPORT,"One exported file system")
,NULL
,helpf
,prompts,tb,NBFIELD,1,nofield)==MENU_ACCEPT){
// Some validation missing ...
tbhost.delall();
path = replaceif (path,tb[F_path]);
comment = replaceif (comment,tb[F_comment]);
int err = 0;
for (i=1; i<F_comment; i++){
if (tb[i][0] != '\0'){
int this_err=0;
tbhost.parse_add (tb[i],this_err);
if (this_err){
nofield = i;
err = 1;
}
}
}
if(!err){
ret = 0;
break;
}
}else{
break;
}
}
return ret;
}
/*
EXPORTS handle the file /etc/exports.
Each line of this file is a EXPORT_FS (including comments).
*/
class EXPORTS: public ARRAY{
/*~PROTOBEG~ EXPORTS */
public:
EXPORTS (void);
int edit (void);
EXPORT_FS *getitem (int no);
void write (void);
/*~PROTOEND~ EXPORTS */
};
PUBLIC EXPORTS::EXPORTS()
{
/* #Specification: /etc/exports / missing
A missing /etc/exports is equivalent to an empty one.
*/
FILE *fin = f_exports.fopen ("r");
if (fin != NULL){
char buf[1000];
int noline = 0;
while (fgets_strip(buf,sizeof(buf)-1,fin,'\\',(char)255,&noline)!=NULL){
if (buf[0] != '\0'){
add (new EXPORT_FS(buf,noline));
}
}
fclose (fin);
}
}
PUBLIC EXPORT_FS *EXPORTS::getitem(int no)
{
return (EXPORT_FS *)ARRAY::getitem(no);
}
PUBLIC void EXPORTS::write ()
{
FILE *fout = f_exports.fopen ("w");
if (fout != NULL){
for (int i=0; i<getnb(); i++) getitem(i)->write(fout);
fclose (fout);
}
}
/*
Edite /etc/exports, return -1 if some error or abort
*/
PUBLIC int EXPORTS::edit()
{
int ret = -1;
int choice=0;
while (1){
int nbp = getnb();
const char **menuopt = new const char *[(nbp)*2+1];
int *tblk = new int [nbp];
int ii=0;
for (int i=0; i<nbp; i++){
EXPORT_FS *fs = getitem(i);
const char *path = fs->getpath();
if (path[0] != '\0'){
tblk[ii/2] = i;
menuopt[ii++] = " ";
menuopt[ii++] = (char*)path;
}
}
menuopt[ii] = NULL;
MENU_STATUS code = xconf_menu (
MSG_U(T_EXPORTED,"Exported file systems")
,MSG_U(I_EXPORTED
,"Setup file systems available for client hosts\n"
"Those systems will be accessible through NFS\n")
,helpf
,ETC_EXPORTS
,NULL
,MSG_U(F_DELETEONE,"to delete one definition")
,MSG_U(F_ADDONE,"to add a new definition")
,menuopt,choice);
free (menuopt);
if (code == MENU_ESCAPE || code == MENU_QUIT){
break;
}else if (code == MENU_ADD){
EXPORT_FS *fs = new EXPORT_FS("",0);
if (fs != NULL && fs->edit()!=-1){
add (fs);
write();
}else{
delete fs;
}
}else{
EXPORT_FS *fs = getitem(tblk[choice]);
if (code == MENU_DEL){
remove (fs);
delete fs;
}else{
// Edit one hosts
if (fs->edit() != -1) write();
}
}
free (tblk);
}
return ret;
}
/*
Edit the file /etc/exports
*/
void exports_edit()
{
EXPORTS exp;
exp.edit();
}
#ifdef TEST
int main(int argc, char *argv[])
{
exports_edit();
}
#endif